package cn.xinfei.xdecision.data.core.frame.executors.http;

import cn.xinfei.xdecision.common.model.datax.consts.VariableConst;
import cn.xinfei.xdecision.common.model.datax.datainterface.v2.VarInterfaceInfo;
import cn.xinfei.xdecision.common.model.datax.datainterface.v2.VariableInterface;
import cn.xinfei.xdecision.common.model.datax.datamanage.v2.VarDataSourceCallLog;
import cn.xinfei.xdecision.common.model.datax.datamanage.v2.VariableCallLog;
import cn.xinfei.xdecision.common.model.datax.enums.*;
import cn.xinfei.xdecision.common.model.datax.exception.XDecisionDataException;
import cn.xinfei.xdecision.common.model.datax.exception.XDecisionDataExceptionType;
import cn.xinfei.xdecision.common.service.datax.datainterface.v2.impl.VarInterfaceInfoServiceImpl;
import cn.xinfei.xdecision.common.service.datax.datainterface.v2.impl.VariableInterfaceServiceImpl;
import cn.xinfei.xdecision.common.service.datax.datamanage.v2.VariableService;
import cn.xinfei.xdecision.data.core.cache.VarInterfaceCache;
import cn.xinfei.xdecision.data.core.cache.VarMetaDataCache;
import cn.xinfei.xdecision.data.core.frame.executors.HttpExecutorFactory;
import cn.xinfei.xdecision.data.core.frame.executors.IExecutorService;
import cn.xinfei.xdecision.data.core.frame.executors.collect.VariableHelper;
import cn.xinfei.xdecision.data.core.frame.listener.VarDataSourceLogEvent;
import cn.xinfei.xdecision.data.core.frame.strategy.StrategyContext;
import cn.xinfei.xdecision.data.core.frame.strategy.VariableStrategy;
import cn.xinfei.xdecision.data.core.util.VarCacheUtils;
import cn.xinfei.xdecision.data.core.util.VarDataSourceCallLogUtil;
import cn.xinfei.xdecision.data.core.util.VariableCallLogUtils;
import cn.xinfei.xdecision.data.core.vo.VarDatasourceDTO;
import cn.xinfei.xdecision.data.core.vo.VarInterfaceInfoVO;
import cn.xinfei.xdecision.data.core.vo.VirtualHttpExecutorResultEntity;
import cn.xinfei.xdecision.data.core.vo.virtual.VirtualVarRequest;
import cn.xinfei.xdecision.data.core.vo.virtual.VirtualVarResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.dianping.cat.Cat;
import com.dianping.cat.message.Transaction;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import java.util.*;


@Service
@Slf4j
public class HttpVirtualExecutorService implements IExecutorService<VirtualHttpExecutorResultEntity> {


    @Autowired
    HttpExecutorFactory httpExecutorFactory;

    @Autowired
    VariableInterfaceServiceImpl variableInterfaceService;

    @Autowired
    VarInterfaceInfoServiceImpl varInterfaceInfoService;

    @Autowired
    VariableService variableService;

    @Autowired
    ApplicationContext applicationContext;

    @Autowired
    VarInterfaceCache varInterfaceCache;

    @Autowired
    VarMetaDataCache varMetaDataCache;

    @Autowired
    StrategyContext strategyContext;


    @Override
    public VirtualHttpExecutorResultEntity run(Set<String> varCodes,
                                               Map<String, Object> inputParam,
                                               Long datasourceId,
                                               CollectStageEnum collectStageEnum,
                                               String requestId) {

        try {
            log.info("requestId = {} dataSourceId = {} start run virtual-http-executor varCodes = {}",
                    requestId,
                    datasourceId.toString(),
                    JSONObject.toJSONString(varCodes));
            VirtualHttpExecutorResultEntity virtualHttpExecutorResultEntity = runVirutalHttpExecutor(varCodes, inputParam, datasourceId, collectStageEnum, requestId);
            return virtualHttpExecutorResultEntity;
        } catch (Exception e) {
            log.error("requestId = {} dataSourceId = {} error in run HttpVirtualExecutor, varCodes = {},inputParam = {}",
                    requestId,
                    datasourceId.toString(),
                    JSONObject.toJSONString(varCodes), JSONObject.toJSONString(inputParam), e);
            VirtualHttpExecutorResultEntity resultEntity = new VirtualHttpExecutorResultEntity();
            resultEntity.setVirtualVarResult(VariableHelper.fillVarWithDefaultValueWhenRespEmpty(variableService.findVariables(varCodes)));
            resultEntity.setCode(String.valueOf(XDecisionDataExceptionType.DATA_HTTP_VIRTUAL_EXECUTOR_ERROR.code));
            resultEntity.setMessage(XDecisionDataExceptionType.DATA_HTTP_VIRTUAL_EXECUTOR_ERROR.text);
            return resultEntity;
        } finally {
            MDC.remove(VariableConst.HANDLE_NAME);
            MDC.remove(VariableConst.COLLECT_STAGE);
            MDC.remove(VariableConst.REQUEST_ID);
            MDC.clear();
        }
    }

    private VirtualHttpExecutorResultEntity runVirutalHttpExecutor(Set<String> varCodes,
                                                                   Map<String, Object> inputParam,
                                                                   Long datasourceId,
                                                                   CollectStageEnum collectStageEnum,
                                                                   String requestId) {

        Transaction catT = Cat.newTransaction(VarCatTypeEnum.VIRTUAL_EXECUTOR_EXECUTE.getCode(), VarCatTypeEnum.VIRTUAL_EXECUTOR_EXECUTE.getName());
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        VirtualHttpExecutorResultEntity executorResultEntity = new VirtualHttpExecutorResultEntity();
        executorResultEntity.setExecType(ExecutorEnum.HTTP.name());
        Optional<List<VariableInterface>> varInterfaceRelCacheLoader = Optional.ofNullable(varInterfaceCache.loadVarInterfaceRelMetaData());
        Optional<Map<Integer, VarInterfaceInfo>> varInterfaceDataCacheLoader = Optional.ofNullable(varInterfaceCache.loadVarInterfaceMetaData());
        List<Long> httpDataSourceIdList;
        List<Long> virtualHttpDataSourceIdList;

        VirtualVarResponse virtualVarResponse = new VirtualVarResponse();
        virtualVarResponse.setRequestId(requestId);
        try {
            if (varInterfaceRelCacheLoader.isPresent()) {
                log.info("requestId = {} dataSourceId = {} HttpVirtualExecuto 查询缓存 VarInterfaceRelDataCache", requestId, datasourceId.toString());
                httpDataSourceIdList = VarCacheUtils.buildHttpDataSourceFromCacheLoader(varCodes, varInterfaceRelCacheLoader);
                virtualHttpDataSourceIdList = VarCacheUtils.buildVirtualDataSourceFromCache(varInterfaceDataCacheLoader, httpDataSourceIdList);

            } else {
                httpDataSourceIdList = variableInterfaceService.selectDatasourceIdsByVarcodes(varCodes);
                virtualHttpDataSourceIdList = varInterfaceInfoService.selectVirtualDataSourceIdsByIds(httpDataSourceIdList);
            }

//            Map<Integer, String> dataSourceIdAndTypeMap = new HashMap<>();
//            if (varInterfaceDataCacheLoader.isPresent()) {
//                log.info("requestId = {} dataSourceId = {} HttpVirtualExecuto 查询缓存 VarInterfaceMetaDataCache", requestId, datasourceId.toString());
//                dataSourceIdAndTypeMap = varInterfaceDataCacheLoader.get()
//                        .values()
//                        .stream()
//                        .collect(Collectors.toMap(VarInterfaceInfo::getId, VarInterfaceInfo::getType, (e1, e2) -> e1));
//            }


            Map<String, Object> executorResult = Maps.newHashMap();
            if (CollectionUtils.isNotEmpty(virtualHttpDataSourceIdList)) {
//                Map<Integer, String> finalDataSourceIdAndTypeMap = dataSourceIdAndTypeMap;
//                Map<VirtualDataSourceEnum, Long> sourceEnumsAndIdMap = virtualHttpDataSourceIdList
//                        .stream()
//                        .collect(Collectors.toMap(dataSourceId ->
//                                        EnumUtil.fromCodeOrThrow(
//                                                VirtualDataSourceEnum.class,
//                                                finalDataSourceIdAndTypeMap.get(dataSourceId.intValue())),
//                                Function.identity(),
//                                (e1, e2) -> e1));
//                sourceEnumsAndIdMap.keySet().forEach(sourceEnum -> {
//                    switch (sourceEnum) {
//                        case JAVA_VAR:
//                        case PYTHON_VAR:
//                        case MODEL_VAR:
//                            //TODO: 对接模型 后期拆分
//                        case CREDIT_VAR:
//                            //TODO: 对接人行 后期拆分
//                            break;
//                        default:
//                            throw new XDecisionDataException(XDecisionDataExceptionType.DATA_NO_SUCH_VAR_VIRTUAL_TYPE_ERROR, "no such virtual type var for: " + sourceEnum.name());
//                    }
//                });
                VarInterfaceInfoVO varInterfaceInfoVO = (VarInterfaceInfoVO) httpExecutorFactory.buildExecutorInput(datasourceId);
                log.info("requestId = {} dataSourceId = {} 开始请求外部数据源 InterfaceName = {},url = {}",
                        requestId,
                        datasourceId.toString(),
                        varInterfaceInfoVO.getName(), varInterfaceInfoVO.getUrl());
                VariableStrategy strategy = strategyContext.getStrategy(VariableStrategyType.HTTP_VITUAL.name());
                VirtualVarRequest virtualVarRequest = buildVirtualVarRequest(varCodes, new JSONObject(inputParam), requestId, varInterfaceInfoVO);
                virtualVarResponse = (VirtualVarResponse) strategy.process(virtualVarRequest);
                executorResult = (Map<String, Object>) virtualVarResponse.getData();
                log.info("requestId = {} virtualDataTransferResponse = {}",
                        requestId,
                        JSONObject.toJSONString(virtualVarResponse.getData(), SerializerFeature.WRITE_MAP_NULL_FEATURES, SerializerFeature.QuoteFieldNames));

            } else {
                log.info("requestId = {} virtualHttpDataSourceIdList is empty", requestId);
            }
            executorResultEntity.setVirtualVarResult(executorResult);
//            List<VariableCallLog> variableCallLogs = VariableCallLogUtils.buildVarCallLogsFromMap(
//                    executorResult,
//                    inputParam,
//                    datasourceId,
//                    VariableEnum.INTERFACE_VAR,
//                    collectStageEnum,
//                    SourceType.THIRD_PARTY,
//                    stopWatch.getStartTime(),
//                    requestId);
//            executorResultEntity.setVarCallLogList(variableCallLogs);
            VarDataSourceCallLog varDataSourceCallLog = VarDataSourceCallLogUtil.buildVarDataSourceCallLog(
                    stopWatch.getStartTime(),
                    System.currentTimeMillis(),
                    JSON.toJSONString(inputParam),
                    JSON.toJSONString(executorResult),
                    requestId,
                    JSON.toJSONString(executorResult),
                    SourceType.THIRD_PARTY.name(),
                    collectStageEnum.getCode(),
                    datasourceId
            );
            //异步保存datasource log
            VarDatasourceDTO varDatasourceDTO = new VarDatasourceDTO();
            varDatasourceDTO.setRequestId(requestId);
            varDatasourceDTO.setVarDataSourceCallLog(varDataSourceCallLog);
            varDatasourceDTO.setCollectStage(collectStageEnum.name());
            applicationContext.publishEvent(new VarDataSourceLogEvent(
                    this, varDatasourceDTO
            ));
            catT.setStatus(Transaction.SUCCESS);
            return executorResultEntity;
        } catch (Exception e) {
            log.error("异常：msg={}", e.getMessage(), e);
            Cat.logError(e);
            catT.setStatus(e);
            throw new XDecisionDataException(XDecisionDataExceptionType.DATA_HTTP_VIRTUAL_EXECUTOR_ERROR, "http-virtual 执行器执行异常", e);
        } finally {
            stopWatch.stop();
            log.info("requestId = {} dataSourceId = {} 任务执行 VirtualHttpDataSource 总耗时:" + stopWatch.getTime() + " MillSeconds", requestId, datasourceId.toString());
            catT.setDurationInMillis(stopWatch.getTime());
            catT.complete();
        }

    }

    private VirtualVarRequest buildVirtualVarRequest(Set<String> varCodes, JSONObject inputParam, String requestId, VarInterfaceInfoVO varInterfaceInfoVO) {
        VirtualVarRequest virtualVarRequest = new VirtualVarRequest();
        virtualVarRequest.setRequestId(requestId);
        virtualVarRequest.setInputParams(inputParam);
        virtualVarRequest.setVarInterfaceInfoVO(varInterfaceInfoVO);
        virtualVarRequest.setVarCodes(new HashSet<>(varCodes));
        return virtualVarRequest;
    }


}
